Imports Enyim.Caching.Memcached
Imports Enyim.Caching
Imports Enyim.Caching.Configuration
Imports System.Net

Namespace Aricie.DNN.Caching.Client.Memcached
    Public Class MemcachedClientProvider
        Inherits CachingClientProvider(Of CachingPortBasedHostInfo, MemcachedClusterSettings)

        Private _MemcachedClient As MemcachedClient

        Private ReadOnly Property MemcachedClient() As MemcachedClient
            Get
                If _MemcachedClient Is Nothing Then

                    Dim mcc As New MemcachedClientConfiguration()

                    Select Case Me.SpecificClusterSettings.NodeLocator
                        Case NodeLocator.DefaultLocator
                            mcc.NodeLocator = GetType(DefaultNodeLocator)
                        Case NodeLocator.SingleNode
                            mcc.NodeLocator = GetType(SingleNodeLocator)
                    End Select

                    Select Case Me.SpecificClusterSettings.KeyTransformer
                        Case KeyTransformer.DefaultKeyTransformer
                            mcc.KeyTransformer = GetType(DefaultKeyTransformer)
                        Case KeyTransformer.Base64
                            mcc.KeyTransformer = GetType(Base64KeyTransformer)
                        Case KeyTransformer.Sha1
                            mcc.KeyTransformer = GetType(SHA1KeyTransformer)
                        Case KeyTransformer.TigerHash
                            mcc.KeyTransformer = GetType(TigerHashKeyTransformer)
                    End Select

                    mcc.Transcoder = GetType(DefaultTranscoder)

                    mcc.SocketPool.MinPoolSize = Me.SpecificClusterSettings.MinPoolSize
                    mcc.SocketPool.MaxPoolSize = Me.SpecificClusterSettings.MaxPoolSize
                    mcc.SocketPool.ConnectionTimeout = Me.SpecificClusterSettings.ClientTimeout.Value
                    mcc.SocketPool.DeadTimeout = Me.SpecificClusterSettings.DeadTimeOut.Value
                    mcc.SocketPool.ReceiveTimeout = Me.SpecificClusterSettings.ReceiveTimeOut.Value


                    For Each objHost As CachingPortBasedHostInfo In Me.SpecificClusterSettings.Hosts
                        Dim addresses As IPAddress() = Dns.GetHostAddresses(objHost.Name)
                        If addresses.Length > 0 Then
                            mcc.Servers.Add(New System.Net.IPEndPoint(addresses(0), objHost.CachePort))
                        End If
                    Next

                    Me._MemcachedClient = New MemcachedClient(mcc)
                    Me.SetCurrentStatus(DistributedCachingStatus.Started)

                End If
                Return _MemcachedClient
            End Get
        End Property

#Region "Overrides"

        Public Overrides Function [Get](ByVal objCacheJob As CacheJob) As WrappedCacheObject
            Dim toReturn As WrappedCacheObject = Nothing
            If objCacheJob.Strategy.ConcurrencyMode = ConcurrencyMode.NoConcurrency Then
                toReturn = Me.MemcachedClient.Get(Of WrappedCacheObject)(objCacheJob.FormattedKey)
            Else
                Dim tempArray() As String = {objCacheJob.FormattedKey}
                Dim casValues As IDictionary(Of String, ULong) = Nothing
                Dim results As IDictionary(Of String, Object) = Me.MemcachedClient.Get(tempArray, casValues)
                If results IsNot Nothing AndAlso results.Count = 1 Then
                    Me.SetItemVersion(objCacheJob.FormattedKey, casValues(objCacheJob.FormattedKey))
                    toReturn = DirectCast(results(objCacheJob.FormattedKey), WrappedCacheObject)
                Else
                    Me.RemoveItemVersion(objCacheJob.FormattedKey)
                End If
            End If
            Return toReturn
        End Function

        Public Overrides Function MultipleGet(ByVal objCacheJob As CacheJob) As System.Collections.Generic.IEnumerable(Of System.Collections.Generic.KeyValuePair(Of String, WrappedCacheObject))
            Dim objKeys As IEnumerable(Of String) = Me.GetTagKeys(objCacheJob)
            Dim result As IDictionary(Of String, Object)
            If objCacheJob.Strategy.ConcurrencyMode = ConcurrencyMode.NoConcurrency Then
                result = Me.MemcachedClient.Get(objKeys)
            Else
                Dim casValues As IDictionary(Of String, ULong) = Nothing
                result = Me.MemcachedClient.Get(objKeys, casValues)
                For Each objPair As KeyValuePair(Of String, ULong) In casValues
                    Me.SetItemVersion(objPair.Key, objPair.Value)
                Next
            End If
            Return New Aricie.Collections.ConvertedEnumerable(Of KeyValuePair(Of String, Object), KeyValuePair(Of String, WrappedCacheObject))(result, AddressOf Me.ConvertObjectToWrappedPair)

        End Function



        Public Overrides Function Put(ByVal objCacheJob As CachePutJob) As Boolean
            Dim toReturn As Boolean
            If objCacheJob.Job.Strategy.ConcurrencyMode = ConcurrencyMode.NoConcurrency Then
                If objCacheJob.Value.AbsoluteExpiration <> System.Web.Caching.Cache.NoAbsoluteExpiration Then
                    toReturn = Me.MemcachedClient.Store(StoreMode.Set, objCacheJob.Job.FormattedKey, objCacheJob.Value, objCacheJob.Value.AbsoluteExpiration)
                ElseIf objCacheJob.Value.SlidingExpriration <> System.Web.Caching.Cache.NoSlidingExpiration Then
                    toReturn = Me.MemcachedClient.Store(StoreMode.Set, objCacheJob.Job.FormattedKey, objCacheJob.Value, objCacheJob.Value.SlidingExpriration)
                Else
                    toReturn = Me.MemcachedClient.Store(StoreMode.Set, objCacheJob.Job.FormattedKey, objCacheJob.Value)
                End If
            Else

                Dim objVersion As Object = Me.GetItemVersion(objCacheJob.Job.FormattedKey)
                If objVersion IsNot Nothing Then
                    Dim cas As ULong = DirectCast(objVersion, ULong)
                    If objCacheJob.Value.AbsoluteExpiration <> System.Web.Caching.Cache.NoAbsoluteExpiration Then
                        toReturn = Me.MemcachedClient.CheckAndSet(objCacheJob.Job.FormattedKey, objCacheJob.Value, cas, objCacheJob.Value.AbsoluteExpiration)
                    ElseIf objCacheJob.Value.SlidingExpriration <> System.Web.Caching.Cache.NoSlidingExpiration Then
                        toReturn = Me.MemcachedClient.CheckAndSet(objCacheJob.Job.FormattedKey, objCacheJob.Value, cas, objCacheJob.Value.SlidingExpriration)
                    Else
                        toReturn = Me.MemcachedClient.CheckAndSet(objCacheJob.Job.FormattedKey, objCacheJob.Value, cas)
                    End If

                ElseIf Me.Get(objCacheJob.Job) Is Nothing Then
                    If objCacheJob.Value.AbsoluteExpiration <> System.Web.Caching.Cache.NoAbsoluteExpiration Then
                        toReturn = Me.MemcachedClient.Store(StoreMode.Add, objCacheJob.Job.FormattedKey, objCacheJob.Value, objCacheJob.Value.AbsoluteExpiration)
                    ElseIf objCacheJob.Value.SlidingExpriration <> System.Web.Caching.Cache.NoSlidingExpiration Then
                        toReturn = Me.MemcachedClient.Store(StoreMode.Add, objCacheJob.Job.FormattedKey, objCacheJob.Value, objCacheJob.Value.SlidingExpriration)
                    Else
                        toReturn = Me.MemcachedClient.Store(StoreMode.Add, objCacheJob.Job.FormattedKey, objCacheJob.Value)
                    End If
                End If
            End If

            Return toReturn
        End Function

        Public Overrides Function AddCallBack(ByVal objCacheJob As CacheJob, ByVal itemRemovedCallback As System.Web.Caching.CacheItemRemovedCallback) As Boolean
            Throw New NotImplementedException("Memcached dot not support notifications")
        End Function

        Public Overrides Sub Remove(ByVal objJob As CacheJob)
            Me.MemcachedClient.Remove(objJob.FormattedKey)
          
        End Sub

        Public Overrides Sub ResetTimeOut(ByVal objCacheJob As CachePutJob)


            Throw New NotImplementedException("Memcached directly supports sliding expiration")

        End Sub

        Public Overrides Sub [Stop]()
            MyBase.Stop()
            Me._MemcachedClient = Nothing
        End Sub

        Public Overrides Sub CreateRegion(ByVal objJob As CacheJob)

            Throw New NotImplementedException("Memcached does not support regions")


        End Sub

        Public Overrides Sub ClearRegion(ByVal objJob As CacheJob)

            Throw New NotImplementedException("Memcached does not support regions")


        End Sub

        Public Overrides Sub DeleteRegion(ByVal objJob As CacheJob)

            Throw New NotImplementedException("Memcached does not support regions")


        End Sub

#End Region

#Region "Private methods"



        'Private Sub ProcessDataCacheException(ByVal dcex As DataCacheException, ByVal objJob As CacheJob)
        '    Dim status As String
        '    Me.AddError(New KeyValuePair(Of String, String)(status, dcex.Message))
        '    If DistributedCachingProvider.Settings.LoggingSettings.EnableDebugLogging Then
        '        Dim vEx As New DistributedCachingException(status, dcex, objJob.Key, objJob.Strategy.RegionName)
        '        Aricie.DNN.Diagnostics.AsyncLogger.Instance.AddException(vEx)
        '    End If
        'End Sub

#End Region

    End Class
End Namespace